/**
 * Copyright 2011 Renato de Jesus Manzoni
 * 
 * SED - Software Engineering and Dependability Research Group
 * 
 *     http://www.sed.ic.unicamp.br/
 * 
 * Distributed Systems Laboratory (LSD) in the Institute of Computing (IC) at University of Campinas (UNICAMP)
 * 
 *     http://www.lsd.ic.unicamp.br/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package br.unicamp.ic.lsd.wscaa;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.ClientCallback;
import org.apache.cxf.service.model.BindingFaultInfo;
import org.apache.cxf.service.model.BindingOperationInfo;

import br.unicamp.ic.lsd.wscaa.exception.WSNotFoundException;
import br.unicamp.ic.lsd.wscaa.interceptors.WSFaultSoapInterceptor;
import br.unicamp.ic.lsd.wscaa.model.ExceptionDispatcher;

/**
 * @author Renato de Jesus Manzoni <rjmanzoni@gmail.com>
 *
 */
public class WebService {
	private final Client client;
	private List<String> exceptions;
	private List<String> operations;
	private static final int THREAD_POOL_SIZE = 10;
	private WSFaultSoapInterceptor interceptor;
	
	
	private static Log logger = LogFactory.getLog(WebService.class);
	
	public WebService(Client client) {
		this.client = client;

		this.client.setExecutor(Executors.newFixedThreadPool(THREAD_POOL_SIZE));
		
		//ok
		interceptor = new WSFaultSoapInterceptor();
		
		//interceptor.setHandler(new ConcurrentExceptionSync(TIME_WINDOW_RESOLUTION, TimeUnit.MILLISECONDS));
		
		this.client.getInFaultInterceptors().add(interceptor);
		
		createExceptionList();
		
		createOperationsSupported();
	}

	public WebService(Client client, ExceptionDispatcher dispatcher){
		this(client);
		
		interceptor.setHandler(dispatcher);
		
	}

	public void invokeAsyncMethod(ClientCallback callback, String method,
			Object... params) throws Exception {
		
		if(logger.isDebugEnabled())
			logger.debug("Invoking the service "+method);
		
			// TODO pre-validation...
			client.invoke(callback, method, params);
			// TODO pos-validation...

	}

	public void invokeAsyncMethod(ClientCallback callback,
			BindingOperationInfo bo, Object[] params) throws Exception {

		client.invoke(callback, bo, params);

	}
	
	// TODO mudar esse metodo pra outra classe?
	public String extractExceptionName(Class<? extends Exception> classEx)
			throws WSNotFoundException {
		String classExException = classEx.toString();

		for (Iterator<String> iterator = exceptions.iterator(); iterator
				.hasNext();) {

			String ex = iterator.next();

			if (classExException.contains(ex))
				return ex;

		}

		throw new WSNotFoundException(
				"Exception not found in the dinamic invokation from ws: "
						+ client.getConduit().getTarget().getAddress()
								.getValue());
	}

	private void createExceptionList() {
		exceptions = new ArrayList<String>();

		Collection<BindingOperationInfo> operations = client.getEndpoint()
				.getBinding().getBindingInfo().getOperations();

		for (Iterator<BindingOperationInfo> iteratorBOI = operations.iterator(); iteratorBOI
				.hasNext();) {
			BindingOperationInfo bindingOperationInfo = iteratorBOI.next();

			Collection<BindingFaultInfo> exceptionsOp = bindingOperationInfo
					.getFaults();

			for (Iterator<BindingFaultInfo> iterator2BFI = exceptionsOp
					.iterator(); iterator2BFI.hasNext();) {

				BindingFaultInfo bindingFaultInfo = iterator2BFI.next();
				exceptions.add(bindingFaultInfo.getFaultInfo().getFaultName()
						.getLocalPart());

			}

		}
	}

	private void createOperationsSupported() {
		
		operations = new ArrayList<String>();
		
		Collection<BindingOperationInfo> collection = client.getEndpoint().getBinding().getBindingInfo().getOperations();
		
		for (Iterator<BindingOperationInfo> iterator = collection.iterator(); iterator.hasNext();) {
			BindingOperationInfo boi = iterator.next();
			operations.add(boi.getName().getLocalPart());
		}
	}
	
	public List<String> getExceptions() {
		return exceptions;
	}

	public void setExceptions(List<String> exceptions) {
		this.exceptions = exceptions;
	}

	public String[] getOperations() {
		return operations.toArray(new String[operations.size()]);
	}
	
	

}